package com.google.code.irouter.batch.api;

import java.io.Serializable;
import java.util.List;

import org.apache.commons.lang.Validate;

import com.google.appengine.api.datastore.Link;
import com.google.appengine.repackaged.com.google.common.base.StringUtil;
import com.google.code.irouter.model.Consumer;
import com.google.code.irouter.model.Model;
import com.google.code.irouter.model.Queue;
import com.google.code.irouter.utils.CacheUtil;
import com.google.code.irouter.utils.LocalUtil;
import com.google.code.irouter.utils.LogUtil;
import com.google.code.irouter.utils.ModelUtil;
import com.google.code.irouter.utils.PropertyUtil;
import com.google.code.irouter.utils.TextUtil;

public abstract class Sender implements Serializable {

	private static final long serialVersionUID = 9136902692818610309L;

	private int repostTimes = 0;

	private boolean success;

	private boolean repost;

	protected abstract void init(Consumer consumer);
	
	protected abstract boolean auth(Consumer consumer) throws Exception;

	protected abstract void send(Consumer consumer, Queue queue, String fitMessage, Link remixedLink, Link file) throws Exception;

	protected abstract boolean isRepost(Exception exception, boolean result);

	public boolean isSuccess() {
		return success;
	}

	protected void setSuccess(boolean success) {
		this.success = success;
	}

	protected boolean isRepost() {
		return repost;
	}

	protected void setRepost(boolean repost) {
		this.repost = repost;
	}

	protected int getRetryTimes() {
		int result = 3;
		String retryTimes = PropertyUtil.getProperty("i-router.retry.times");
		if (retryTimes != null) {
			result = Integer.valueOf(retryTimes);
		}
		return result;
	}

	private Link getRemixLink(Link link, Link fixedLink) {
		String result = "";
		boolean isFirst = false;
		try {
			Validate.notNull(link);
			result = link.getValue();
		} catch (IllegalArgumentException e) {
			isFirst = true;
		}
		try {
			Validate.notNull(fixedLink);
			if (isFirst)
				result = fixedLink.getValue();
			else
				result = TextUtil.contact(
							true, 
							result, 
							fixedLink.getValue()
						);
		} catch (IllegalArgumentException e) {
		}
		String baseUrl = PropertyUtil.getProperty("i-router.default.proxy.page");
		return new Link(
					TextUtil.contact(
							baseUrl, 
							TextUtil.encodeBASE64(result)
					)
				);
	}

	private void process(Queue queue) {
		setSuccess(true);
		setRepost(false);
		List<Model> modelList = ModelUtil.select(Consumer.class, queue.getConsumer());
		if(modelList.size() > 0) {
			Consumer consumer = (Consumer) modelList.get(0);
			init(consumer);
	
			try {
				if (CacheUtil.containsKey(getKey(consumer))) {
					Sender sender = (Sender) CacheUtil.get(getKey(consumer));
					String cutted = TextUtil.cut(
							queue.getMessage(), consumer.getMaxLength());
					if(!cutted.equals(queue.getMessage())) {
						cutted += "...";
					}
					sender.send(consumer, queue, cutted,
							getRemixLink(queue.getLink(), queue.getFixedLink()), queue.getFile());
					LogUtil.info(
							LogUtil.TYPE.BATCH, 
							Sender.class, 
							queue.getSolution(),
							LocalUtil
							.getResource("com.google.code.irouter.batch.api.sender.send.success")
					);
				} else {
					if (auth(consumer)) {
						LogUtil.info(
								LogUtil.TYPE.BATCH, 
								Sender.class, 
								queue.getSolution(),
								LocalUtil.getResource("com.google.code.irouter.batch.api.sender.auth.success")
						);
						save(consumer);
						send(
								consumer, 
								queue, 
								StringUtil.truncateAtMaxLength(queue.getMessage(), consumer.getMaxLength(), true),
								getRemixLink(queue.getLink(), queue.getFixedLink()), queue.getFile()
						);
						LogUtil.info(
								LogUtil.TYPE.BATCH, 
								Sender.class, 
								queue.getSolution(),
								LocalUtil.getResource("com.google.code.irouter.batch.api.sender.send.success")
						);
					} else {
						LogUtil.warn(
								LogUtil.TYPE.BATCH, 
								Sender.class, 
								queue.getSolution(),
								LocalUtil.getResource("com.google.code.irouter.batch.api.sender.auth.fail")
						);
						setSuccess(false);
					}
				}
			} catch (Exception exception) {
				setSuccess(false);
				setRepost(isRepost(exception, false));
				LogUtil.warn(
						LogUtil.TYPE.BATCH, 
						Sender.class,
						queue.getSolution(),
						LocalUtil.getResource("com.google.code.irouter.batch.api.sender.send.fail"),
						exception
				);
			}
		} else {
			LogUtil.warn(
					LogUtil.TYPE.BATCH, 
					Sender.class,
					queue.getSolution(),
					LocalUtil.getResource("com.google.code.irouter.batch.api.sender.consumer.removed")
			);
			setSuccess(false);
			setRepost(false);
			
			LogUtil.warn(
					LogUtil.TYPE.BATCH, 
					Sender.class,
					queue.getSolution(),
					LocalUtil.getResource("com.google.code.irouter.batch.api.sender.queue.missing.sender")
			);
			ModelUtil.remove(queue);
		}

	}

	public void execute(Queue queue) {
		process(queue);
		if (!isSuccess())
			if (this.repost)
				if (this.repostTimes++ < getRetryTimes())
					execute(queue);
	}

	private void save(Consumer consumer) {
		CacheUtil.put(getKey(consumer), this);
	}

	private String getKey(Consumer consumer) {
		return TextUtil.contact(
					true, 
					this.getClass().getName(),
					consumer.getConsumerKey(), 
					consumer.getConsumerSecret(),
					consumer.getUser()
				);
	}

}
